package com.usbserial.sample;

import android.hardware.usb.UsbDevice;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.text.format.DateFormat;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.usbserial.core.DataListener;
import com.usbserial.core.SerialConfig;
import com.usbserial.core.SerialPort;
import com.usbserial.core.UsbPermissionCallback;
import com.usbserial.core.UsbSerialDeviceInfo;
import com.usbserial.core.UsbSerialManager;
import com.usbserial.exception.ParseException;
import com.usbserial.exception.UsbSerialException;
import com.usbserial.scale.ScaleData;
import com.usbserial.scale.adapter.ScaleAdapter;
import com.usbserial.scale.adapter.ScaleAdapterManager;
import com.usbserial.scale.adapter.aclas.AclasOS2XAdapter;
import com.usbserial.scale.adapter.aclas.AclasOSX15Adapter;
import com.usbserial.scale.adapter.mettler.MettlerToledoAdapter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.io.ByteArrayOutputStream;

/**
 * 原生演示界面：连接 USB 称重设备并显示重量。
 */
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "SCALE_DEBUG";
    private static final int WEIGH_BAUD_RATE = 9600;
    private static final String APP_VERSION = "0.0.9-OS2X-REFACTOR";
    private static final String PREF_NAME = "scale_preferences";
    private static final String PREF_KEY_ADAPTER_TYPE = "selected_adapter_type";

    // 适配器类型枚举
    public enum AdapterType {
        ACLAS_OS2X("Aclas OS2X", AclasOS2XAdapter.class),
        ACLAS_OSX15("Aclas OSX-15", AclasOSX15Adapter.class),
        METTLER_TOLEDO("Mettler Toledo MT-SICS", MettlerToledoAdapter.class);

        private final String displayName;
        private final Class<? extends ScaleAdapter> adapterClass;

        AdapterType(String displayName, Class<? extends ScaleAdapter> adapterClass) {
            this.displayName = displayName;
            this.adapterClass = adapterClass;
        }

        public String getDisplayName() {
            return displayName;
        }

        public Class<? extends ScaleAdapter> getAdapterClass() {
            return adapterClass;
        }

        @Override
        public String toString() {
            return displayName;
        }

        public static AdapterType fromName(String name) {
            for (AdapterType type : values()) {
                if (type.name().equals(name)) {
                    return type;
                }
            }
            return ACLAS_OS2X; // 默认返回 OS2X
        }
    }

    // 设备类型枚举（保留用于其他用途）
    public enum DeviceType {
        AUTO("自动检测", null),
        ACLAS("Aclas OS2X/OSX-15", AclasOS2XAdapter.class),
        METTLER("Mettler Toledo MT-SICS", MettlerToledoAdapter.class);

        private final String displayName;
        private final Class<? extends ScaleAdapter> adapterClass;

        DeviceType(String displayName, Class<? extends ScaleAdapter> adapterClass) {
            this.displayName = displayName;
            this.adapterClass = adapterClass;
        }

        public String getDisplayName() {
            return displayName;
        }

        public Class<? extends ScaleAdapter> getAdapterClass() {
            return adapterClass;
        }

        @Override
        public String toString() {
            return displayName;
        }
    }

    private static final Map<Integer, int[]> SUPPORTED_DEVICES = new HashMap<>();

    static {
        SUPPORTED_DEVICES.put(1027, new int[]{24577, 24592, 24593, 24596, 24597});
        SUPPORTED_DEVICES.put(4292, new int[]{60000, 60016, 60017});
        SUPPORTED_DEVICES.put(1659, new int[]{8963, 9123, 9139, 9155, 9171, 9187, 9203});
        SUPPORTED_DEVICES.put(6790, new int[]{21795, 29987, 21972});
        SUPPORTED_DEVICES.put(9025, new int[]{}); // 接受所有 PID
        SUPPORTED_DEVICES.put(5824, new int[]{1155});
        SUPPORTED_DEVICES.put(1003, new int[]{8260});
        SUPPORTED_DEVICES.put(7855, new int[]{4});
        SUPPORTED_DEVICES.put(3368, new int[]{516});
        SUPPORTED_DEVICES.put(1155, new int[]{22336});
        SUPPORTED_DEVICES.put(11914, new int[]{5, 10});
    }

    private TextView statusView;
    private TextView weightView;
    private Spinner deviceTypeSpinner;
    private Button connectButton;
    private Button stopButton;
    private Button loopStartButton;
    private Button loopStopButton;
    private Button clearLogButton;
    private ScrollView logScrollView;
    private TextView logView;

    private final StringBuilder logBuffer = new StringBuilder();
    private static final int MAX_LOG_CHARS = 8000;
    private long lastRawLogMillis = 0L;
    private static final long RAW_LOG_THROTTLE_MS = 3000; // 每 3 秒输出一次原始日志
    private long lastWeightLogMillis = 0L;
    private String lastWeightLogMessage = "";
    private static final long WEIGHT_LOG_THROTTLE_MS = 1000;

    private UsbSerialManager serialManager;
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    private final Handler mainHandler = new Handler(Looper.getMainLooper());

    private final Object portLock = new Object();
    @Nullable
    private SerialPort activePort;
    @Nullable
    private UsbSerialDeviceInfo activeDevice;
    @Nullable
    private UsbSerialDeviceInfo pendingDevice;
    @Nullable
    private ScaleAdapter activeAdapter;

    private final FrameAccumulator frameAccumulator = new FrameAccumulator();

    private volatile boolean connecting = false;
    private ScheduledExecutorService handshakeScheduler;

    private final DataListener dataListener = new DataListener() {
        @Override
        public void onDataReceived(byte[] data) {
            appendRawLog(data);
            List<byte[]> frames = frameAccumulator.append(data);
            for (byte[] frame : frames) {
                handleFrame(frame);
            }
        }

        @Override
        public void onError(Exception error) {
            appendLog("串口异常: " + error.getMessage());
            postToast("串口异常: " + error.getMessage());
        }
    };

    private final UsbPermissionCallback permissionCallback = new UsbPermissionCallback() {
        @Override
        public void onPermissionGranted(UsbDevice device) {
            UsbSerialDeviceInfo target = pendingDevice;
            if (target == null || target.getUsbDevice().getDeviceId() != device.getDeviceId()) {
                target = new UsbSerialDeviceInfo(device, com.usbserial.driver.DriverType.AUTO);
            }
            final UsbSerialDeviceInfo finalTarget = target;
            connecting = true;
            updateStatus(getString(R.string.status_connecting));
            appendLog("已获取 USB 权限");
            updateButtons();
            executor.execute(() -> openSerialPort(finalTarget));
        }

        @Override
        public void onPermissionDenied(UsbDevice device) {
            connecting = false;
            updateStatus(getString(R.string.status_disconnected));
            postToast("设备未授权");
            appendLog("用户拒绝 USB 权限");
            updateButtons();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusView = findViewById(R.id.txt_status);
        weightView = findViewById(R.id.txt_weight);
        connectButton = findViewById(R.id.btn_connect);
        stopButton = findViewById(R.id.btn_stop);
        loopStartButton = findViewById(R.id.btn_loop_start);
        loopStopButton = findViewById(R.id.btn_loop_stop);
        clearLogButton = findViewById(R.id.btn_clear_log);

        serialManager = new UsbSerialManager(getApplicationContext());

        logScrollView = findViewById(R.id.scroll_log);
        logView = findViewById(R.id.txt_log);

        appendLog("应用启动，版本=" + APP_VERSION);
        TextView versionView = findViewById(R.id.txt_version);
        versionView.setText(getString(R.string.version_format, APP_VERSION));

        // 初始化 Spinner
        deviceTypeSpinner = findViewById(R.id.spinner_device_type);
        initializeAdapterSpinner();

        connectButton.setOnClickListener(v -> connectToScale());
        stopButton.setOnClickListener(v -> stopWeighing());
        loopStartButton.setOnClickListener(v -> toggleHandshakeLoop(true));
        loopStopButton.setOnClickListener(v -> toggleHandshakeLoop(false));
        clearLogButton.setOnClickListener(v -> clearLog());
        updateButtons();
        autoConnectDefaultDevice();
    }

    /**
     * 初始化适配器选择Spinner
     */
    private void initializeAdapterSpinner() {
        // 创建适配器选项列表
        ArrayAdapter<AdapterType> adapter = new ArrayAdapter<>(
                this,
                android.R.layout.simple_spinner_item,
                AdapterType.values()
        );
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        deviceTypeSpinner.setAdapter(adapter);

        // 从 SharedPreferences 读取保存的选择
        android.content.SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
        String savedAdapterName = prefs.getString(PREF_KEY_ADAPTER_TYPE, AdapterType.ACLAS_OS2X.name());
        AdapterType savedAdapter = AdapterType.fromName(savedAdapterName);

        // 设置Spinner的默认选择
        deviceTypeSpinner.setSelection(savedAdapter.ordinal());

        // 监听选择变化
        deviceTypeSpinner.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(android.widget.AdapterView<?> parent, android.view.View view, int position, long id) {
                AdapterType selectedType = (AdapterType) parent.getItemAtPosition(position);
                // 保存选择到 SharedPreferences
                android.content.SharedPreferences.Editor editor = prefs.edit();
                editor.putString(PREF_KEY_ADAPTER_TYPE, selectedType.name());
                editor.apply();
                appendLog("已选择适配器: " + selectedType.getDisplayName());
            }

            @Override
            public void onNothingSelected(android.widget.AdapterView<?> parent) {
                // Do nothing
            }
        });
    }

    /**
     * 获取当前选择的适配器类型
     */
    private AdapterType getSelectedAdapterType() {
        android.content.SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
        String savedAdapterName = prefs.getString(PREF_KEY_ADAPTER_TYPE, AdapterType.ACLAS_OS2X.name());
        return AdapterType.fromName(savedAdapterName);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopHandshakeLoop();
        stopHandshakeLoop();
        stopHandshakeLoop();
        stopWeighing();
        executor.shutdownNow();
        try {
            serialManager.close();
        } catch (IOException ignored) {
        }
    }

    private void connectToScale() {
        if (connecting) {
            appendLog("已有连接任务在进行，忽略重复请求");
            return;
        }
        connecting = true;
        updateStatus(getString(R.string.status_connecting));
        appendLog("开始扫描称重设备...");
        updateButtons();
        executor.execute(() -> {
            try {
                List<UsbSerialDeviceInfo> devices = serialManager.listDevices();
                UsbSerialDeviceInfo target = findSupportedDevice(devices);
                if (target == null) {
                    connecting = false;
                    updateStatus(getString(R.string.status_disconnected));
                    postToast("未找到称重设备");
                    appendLog("未找到匹配的称重设备");
                    return;
                }
                // 输出详细的设备信息到 Log
                UsbDevice device = target.getUsbDevice();
                Log.d(TAG, "=== USB Device Info ===");
                Log.d(TAG, "VID: " + formatHex(target.getVendorId()));
                Log.d(TAG, "PID: " + formatHex(target.getProductId()));
                Log.d(TAG, "Device Class: " + device.getDeviceClass());
                Log.d(TAG, "Device Subclass: " + device.getDeviceSubclass());
                Log.d(TAG, "Device Protocol: " + device.getDeviceProtocol());
                Log.d(TAG, "Interface Count: " + device.getInterfaceCount());
                Log.d(TAG, "Device Name: " + device.getDeviceName());

                appendLog(String.format(Locale.getDefault(),
                        "发现设备 VID=%s PID=%s", formatHex(target.getVendorId()),
                        formatHex(target.getProductId())));
                if (!serialManager.hasPermission(target.getUsbDevice())) {
                    pendingDevice = target;
                    serialManager.requestPermission(target.getUsbDevice(), permissionCallback);
                    updateStatus(getString(R.string.status_waiting_permission));
                    appendLog("请求 USB 权限，等待用户授权...");
                } else {
                    openSerialPort(target);
                }
            } catch (Exception e) {
                connecting = false;
                updateStatus(getString(R.string.status_disconnected));
                postToast("连接失败: " + e.getMessage());
                appendLog("连接失败: " + e.getMessage());
            }
        });
    }

    private void openSerialPort(UsbSerialDeviceInfo deviceInfo) {
        try {
            SerialConfig config = SerialConfig.builder()
                    .baudRate(WEIGH_BAUD_RATE)
                    .dataBits(SerialConfig.DATABITS_8)
                    .stopBits(SerialConfig.STOPBITS_1)
                    .parity(SerialConfig.PARITY_NONE)
                    .flowControl(SerialConfig.FLOW_CONTROL_OFF)
                    .build();
            SerialPort port = serialManager.openPort(deviceInfo, config);
            port.addDataListener(dataListener);
            boolean opened = port.open();
            if (opened) {
                replacePort(port, deviceInfo);
                frameAccumulator.reset();

                // 根据用户选择创建适配器
                AdapterType selectedType = getSelectedAdapterType();
                ScaleAdapter adapter = ScaleAdapterManager.createAdapterByType(
                        selectedType.getAdapterClass(), deviceInfo, port);
                if (adapter != null) {
                    synchronized (portLock) {
                        activeAdapter = adapter;
                    }
                    appendLog("使用适配器: " + adapter.getName() + " v" + adapter.getVersion());
                    Log.d(TAG, "适配器已创建: " + adapter.getName());
                } else {
                    appendLog("错误: 适配器创建失败");
                    Log.e(TAG, "适配器创建失败");
                }

                try {
                    port.setDTR(true);
                    port.setRTS(true);
                    appendLog("已设置 DTR/RTS 为 true");
                } catch (UsbSerialException e) {
                    appendLog("设置 DTR/RTS 失败: " + e.getMessage());
                }
                updateStatus(getString(R.string.status_connected,
                        formatHex(deviceInfo.getVendorId()), formatHex(deviceInfo.getProductId())));
                updateWeightDisplay(getString(R.string.weight_placeholder));
                appendLog(String.format(Locale.getDefault(),
                        "串口已打开，参数=%d/8N1", WEIGH_BAUD_RATE));
                updateButtons();
            } else {
                port.removeDataListener(dataListener);
                port.close();
                connecting = false;
                updateStatus(getString(R.string.status_disconnected));
                postToast("串口打开失败");
                appendLog("串口打开失败");
                updateButtons();
            }
        } catch (IOException | UsbSerialException e) {
            connecting = false;
            updateStatus(getString(R.string.status_disconnected));
            postToast("连接失败: " + e.getMessage());
            appendLog("连接失败: " + e.getMessage());
            updateButtons();
        }
    }

    private void replacePort(SerialPort newPort, UsbSerialDeviceInfo deviceInfo) {
        synchronized (portLock) {
            if (activePort != null) {
                activePort.removeDataListener(dataListener);
                try {
                    activePort.close();
                } catch (IOException ignored) {
                }
            }
            activePort = newPort;
            activeDevice = deviceInfo;
            pendingDevice = null;
            connecting = false;
        }
    }

    private void stopWeighing() {
        executor.execute(() -> {
            SerialPort port;
            synchronized (portLock) {
                port = activePort;
                activePort = null;
                activeDevice = null;
            }
            if (port != null) {
                port.removeDataListener(dataListener);
                try {
                    port.close();
                } catch (IOException ignored) {
                }
            }
            frameAccumulator.reset();
            connecting = false;
            appendLog("已断开连接");
            updateStatus(getString(R.string.status_disconnected));
            updateWeightDisplay(getString(R.string.weight_placeholder));
            updateButtons();
        });
    }

    private void sendAsciiCommand(String command, String label) {
        byte[] payload;
        try {
            payload = parseAsciiEscapes(command);
        } catch (IllegalArgumentException e) {
            appendLog("命令解析失败: " + e.getMessage());
            postToast("命令格式错误");
            return;
        }
        sendCommandInternal(payload, String.format(Locale.getDefault(), "%s (ASCII)", label));
    }

    private void sendHexCommand(String command, String label) {
        byte[] payload;
        try {
            payload = parseHexString(command);
        } catch (IllegalArgumentException e) {
            appendLog("命令解析失败: " + e.getMessage());
            postToast("命令格式错误");
            return;
        }
        sendCommandInternal(payload, String.format(Locale.getDefault(), "%s (HEX)", label));
    }

    private void sendCommandInternal(byte[] payload, String description) {
        SerialPort port;
        synchronized (portLock) {
            port = activePort;
        }
        if (port == null) {
            appendLog(getString(R.string.log_no_port));
            postToast("未连接串口");
            return;
        }
        final SerialPort targetPort = port;
        executor.execute(() -> {
            try {
                targetPort.write(payload);
                appendLog(String.format(Locale.getDefault(),
                        "发送命令 %s: ASCII=%s",
                        description,
                        bytesToPrintableAscii(payload)));
            } catch (UsbSerialException e) {
                appendLog("发送命令失败: " + e.getMessage());
                postToast("发送失败: " + e.getMessage());
            }
        });
    }

    private void clearLog() {
        mainHandler.post(() -> {
            logBuffer.setLength(0);
            logView.setText("");
        });
    }

    private void runHandshakeSequence() {
        sendHexCommand("05", "ENQ");
        mainHandler.postDelayed(() -> sendAsciiCommand("SI\r\n", "SI"), 200);
        mainHandler.postDelayed(() -> sendAsciiCommand("W\r\n", "W"), 400);
    }

    private void toggleHandshakeLoop(boolean enable) {
        if (enable) {
            if (handshakeScheduler != null && !handshakeScheduler.isShutdown()) {
                appendLog("握手循环已在运行");
                updateButtons();
                return;
            }
            appendLog("开始循环发送握手指令 (ENQ → SI → W)");
            handshakeScheduler = Executors.newSingleThreadScheduledExecutor();
            handshakeScheduler.scheduleAtFixedRate(this::runHandshakeSequence, 0, 2, TimeUnit.SECONDS);
            updateButtons();
        } else {
            stopHandshakeLoop();
        }
    }

    private void stopHandshakeLoop() {
        if (handshakeScheduler != null) {
            handshakeScheduler.shutdownNow();
            handshakeScheduler = null;
            appendLog("已停止循环握手");
        }
        updateButtons();
    }

    private void handleFrame(byte[] frame) {
        Log.d(TAG, "handleFrame called with frame length: " + frame.length);
        ScaleAdapter adapter;
        synchronized (portLock) {
            adapter = activeAdapter;
        }

        if (adapter == null) {
            appendLog("错误: 适配器未初始化");
            return;
        }

        try {
            ScaleData scaleData = adapter.parseData(frame);
            String statusStr;
            switch (scaleData.getStatus()) {
                case STABLE:
                    statusStr = "稳定";
                    break;
                case UNSTABLE:
                    statusStr = "不稳定";
                    break;
                case ZERO:
                    statusStr = "零点";
                    break;
                case OVERLOAD:
                    statusStr = "超载";
                    break;
                default:
                    statusStr = scaleData.getStatus().name();
            }
            final String message = String.format(Locale.getDefault(),
                    "称重数据: %.3f %s (%s)",
                    scaleData.getWeight(),
                    scaleData.getUnit().name().toLowerCase(),
                    statusStr);
            Log.d(TAG, message);
            updateWeightDisplay(message);
        } catch (ParseException e) {
            Log.e(TAG, "数据解析失败: " + e.getMessage());
            appendLog("数据解析失败: " + e.getMessage());
        }
    }

    private void updateStatus(String text) {
        mainHandler.post(() -> statusView.setText(text));
    }

    private void updateWeightDisplay(String text) {
        mainHandler.post(() -> weightView.setText(text));
    }

    private void postToast(String message) {
        mainHandler.post(() -> Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show());
    }

    private void appendLog(String message) {
        // 同时输出到 Android Log 系统，方便 adb logcat 抓取
        Log.d(TAG, message);

        final String line = String.format(Locale.getDefault(), "%s - %s\n",
                DateFormat.format("HH:mm:ss", System.currentTimeMillis()), message);
        mainHandler.post(() -> {
            logBuffer.append(line);
            if (logBuffer.length() > MAX_LOG_CHARS) {
                logBuffer.delete(0, logBuffer.length() - MAX_LOG_CHARS);
            }
            logView.setText(logBuffer.toString());
            if (logScrollView != null) {
                logScrollView.post(() -> logScrollView.fullScroll(ScrollView.FOCUS_DOWN));
            }
        });
    }

    private void appendRawLog(byte[] data) {
        long now = System.currentTimeMillis();
        if (now - lastRawLogMillis < RAW_LOG_THROTTLE_MS) {
            return;
        }
        lastRawLogMillis = now;

        // 输出详细的原始数据到 Log
        String hexStr = bytesToHex(data);
        String asciiStr = bytesToPrintableAscii(data);
        Log.d(TAG, "=== Raw Data ===");
        Log.d(TAG, "Length: " + data.length);
        Log.d(TAG, "Hex: " + hexStr);
        Log.d(TAG, "ASCII: " + asciiStr);

        appendDetailLog("称重日志", asciiStr);
    }

    private void appendDetailLog(String prefix, String ascii) {
        appendLog(String.format(Locale.getDefault(), "%s ASCII=%s", prefix, ascii));
    }


    private void updateButtons() {
        boolean hasPort;
        synchronized (portLock) {
            hasPort = activePort != null;
        }
        final boolean running = handshakeScheduler != null && !handshakeScheduler.isShutdown();
        mainHandler.post(() -> {
            connectButton.setEnabled(!connecting && !hasPort);
            stopButton.setEnabled(hasPort);
            loopStartButton.setEnabled(!running);
            loopStopButton.setEnabled(running);
        });
    }

    private String bytesToHex(byte[] data) { // no longer used but kept if needed in future
        StringBuilder builder = new StringBuilder();
        int limit = Math.min(data.length, 64);
        for (int i = 0; i < limit; i++) {
            builder.append(String.format(Locale.getDefault(), "%02X ", data[i]));
        }
        if (data.length > limit) {
            builder.append("...");
        }
        return builder.toString().trim();
    }

    private String bytesToPrintableAscii(byte[] data) {
        StringBuilder builder = new StringBuilder();
        int limit = Math.min(data.length, 64);
        for (int i = 0; i < limit; i++) {
            int value = data[i] & 0xFF;
            if (value >= 32 && value <= 126) {
                builder.append((char) value);
            } else {
                builder.append('.');
            }
        }
        if (data.length > limit) {
            builder.append("...");
        }
        return builder.toString();
    }


    private void autoConnectDefaultDevice() {
        executor.execute(() -> {
            if (connecting || activePort != null) {
                return;
            }
            try {
                List<UsbSerialDeviceInfo> devices = serialManager.listDevices();

                // 记录所有检测到的USB设备
                appendLog(String.format(Locale.getDefault(), "检测到 %d 个 USB 设备", devices.size()));
                for (UsbSerialDeviceInfo device : devices) {
                    appendLog(String.format(Locale.getDefault(),
                            "设备: VID=%s PID=%s Class=%d Subclass=%d Protocol=%d",
                            formatHex(device.getVendorId()),
                            formatHex(device.getProductId()),
                            device.getUsbDevice().getDeviceClass(),
                            device.getUsbDevice().getDeviceSubclass(),
                            device.getUsbDevice().getDeviceProtocol()));
                }

                UsbSerialDeviceInfo target = findSupportedDevice(devices);
                if (target == null) {
                    appendLog("未找到默认称重设备");
                    updateButtons();
                    return;
                }
                appendLog(String.format(Locale.getDefault(),
                        "自动检测到设备 VID=%s PID=%s",
                        formatHex(target.getVendorId()), formatHex(target.getProductId())));
                if (!serialManager.hasPermission(target.getUsbDevice())) {
                    pendingDevice = target;
                    connecting = true;
                    updateStatus(getString(R.string.status_waiting_permission));
                    appendLog("自动申请 USB 权限...");
                    updateButtons();
                    serialManager.requestPermission(target.getUsbDevice(), permissionCallback);
                } else {
                    connecting = true;
                    updateStatus(getString(R.string.status_connecting));
                    appendLog("自动连接默认设备...");
                    updateButtons();
                    openSerialPort(target);
                }
            } catch (Exception e) {
                connecting = false;
                appendLog("自动连接失败: " + e.getMessage());
                updateButtons();
            }
        });
    }

    private byte[] parseAsciiEscapes(String text) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            if (c == '\\' && i + 1 < text.length()) {
                char next = text.charAt(++i);
                switch (next) {
                    case 'n':
                        baos.write('\n');
                        break;
                    case 'r':
                        baos.write('\r');
                        break;
                    case 't':
                        baos.write('\t');
                        break;
                    case '\\':
                        baos.write('\\');
                        break;
                    case 'x':
                        if (i + 2 < text.length()) {
                            String hex = text.substring(i + 1, i + 3);
                            baos.write(Integer.parseInt(hex, 16));
                            i += 2;
                        } else {
                            throw new IllegalArgumentException("\\x 后缺少16进制数");
                        }
                        break;
                    default:
                        baos.write(next);
                        break;
                }
            } else {
                baos.write(c);
            }
        }
        return baos.toByteArray();
    }

    private byte[] parseHexString(String text) {
        String cleaned = text.replaceAll("[,;]", " ").trim();
        if (cleaned.isEmpty()) {
            throw new IllegalArgumentException("HEX 字符串为空");
        }
        String[] parts = cleaned.split("\\s+");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        for (String part : parts) {
            if (part.isEmpty()) {
                continue;
            }
            baos.write(Integer.parseInt(part, 16));
        }
        return baos.toByteArray();
    }

    private String formatHex(int value) {
        return String.format(Locale.getDefault(), "0x%04X", value);
    }

    @Nullable
    private UsbSerialDeviceInfo findSupportedDevice(List<UsbSerialDeviceInfo> devices) {
        for (UsbSerialDeviceInfo info : devices) {
            if (isSupported(info)) {
                return info;
            }
        }
        return null;
    }

    private boolean isSupported(UsbSerialDeviceInfo info) {
        int[] products = SUPPORTED_DEVICES.get(info.getVendorId());
        if (products == null) {
            return false;
        }
        if (products.length == 0) {
            return true;
        }
        int pid = info.getProductId();
        for (int product : products) {
            if (product == pid) {
                return true;
            }
        }
        return false;
    }

    /**
     * 按照 Flutter 版中的 WindowStrategy 逻辑对字节流进行分帧。
     */
    private static class FrameAccumulator {
        private final Deque<Byte> startWindow = new ArrayDeque<>();
        private final List<Byte> frameBuffer = new ArrayList<>();
        private boolean capturing = false;

        synchronized List<byte[]> append(byte[] chunk) {
            List<byte[]> frames = new ArrayList<>();
            for (byte b : chunk) {
                processByte(b, frames);
            }
            return frames;
        }

        synchronized void reset() {
            startWindow.clear();
            frameBuffer.clear();
            capturing = false;
        }

        private void processByte(byte value, List<byte[]> frames) {
            if (!capturing) {
                startWindow.addLast(value);
                if (startWindow.size() > 3) {
                    startWindow.removeFirst();
                }
                if (startWindow.size() == 3 && isFrameStart()) {
                    Log.d(TAG, "Frame start detected");
                    capturing = true;
                    frameBuffer.clear();
                    frameBuffer.addAll(startWindow);
                    startWindow.clear();
                }
            } else {
                frameBuffer.add(value);
                // 帧格式: [SOH][STX][Data 11 bytes][ETX][EOT][Checksum] = 16 bytes total
                // 需要在检测到 ETX EOT 后再读取一个校验字节
                if (frameBuffer.size() >= 16) {
                    Log.d(TAG, "Frame buffer reached 16 bytes, checking if complete");
                    if (isFrameComplete()) {
                        Log.d(TAG, "Frame complete! Extracting frame");
                        byte[] frame = new byte[frameBuffer.size()];
                        for (int i = 0; i < frameBuffer.size(); i++) {
                            frame[i] = frameBuffer.get(i);
                        }
                        frames.add(frame);
                        frameBuffer.clear();
                        capturing = false;
                    } else {
                        Log.d(TAG, "Frame not complete, waiting for more bytes");
                    }
                }
            }
        }

        private boolean isFrameStart() {
            if (startWindow.size() < 3) {
                return false;
            }
            Byte[] arr = startWindow.toArray(new Byte[0]);
            int first = arr[0] & 0xFF;
            int second = arr[1] & 0xFF;
            int third = arr[2] & 0xFF;
            return first == 0x01
                    && second == 0x02
                    && (third == 0x53 || third == 0x55);
        }

        private boolean isFrameComplete() {
            // 帧必须是16字节，且倒数第3和第2个字节是 ETX (0x03) 和 EOT (0x04)
            if (frameBuffer.size() != 16) {
                return false;
            }
            int etx = frameBuffer.get(13) & 0xFF;  // 倒数第3个字节
            int eot = frameBuffer.get(14) & 0xFF;  // 倒数第2个字节
            return etx == 0x03 && eot == 0x04;
        }
    }
}
